package org.xtlgen;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Element;

/**
 * Represents a Polygon in the XtlGen system.
 * <p>
 * A Polygon must be specified like this:
 * </p>
 * <pre>
 * &lt;Polygon&gt;
 *   &lt;Vertice1 x="[x_position]" y="[y_position]"/&gt;
 *   &lt;Vertice2 x="[x_position]" y="[y_position]"/&gt;
 *   &lt;Vertice3 x="[x_position]" y="[y_position]"/&gt;
 *   [more vertices]
 *   [subshapes]
 * &lt;/Polygon&gt;
 * </pre>
 * in the configuration file.
 */
public class Polygon extends ClosedShape {
  /**
   * Construct a Polygon.
   *
   * @param parent   the parent Shape, null allowed
   * @param vertices the vertices for the polygon
   */
  public Polygon(Shape parent, Collection<Vertice> vertices) {
    super(parent, makePolygon(vertices));
  }

  /**
   * Construct a Polygon.
   *
   * @param parent  the parent Shape, null allowed
   * @param params  parameters for this Shape as read from the config file
   */
  public Polygon(Shape parent, Element params) {
    super(parent, params);
  }
  
  /*
   * Make an AWT polygon from an ArrayList of vertices
   */
  static private java.awt.Shape makePolygon(Collection<Vertice> vertices) {
    GeneralPath result = null;
    
    if ((vertices == null) || (vertices.size() < 3)) {
      throw new IllegalArgumentException("Polygon requires at least 3 vertices.");
    }

    Iterator<Vertice> it = vertices.iterator();
    Vertice v = it.next();
    
    result = new GeneralPath(GeneralPath.WIND_EVEN_ODD, vertices.size());

    result.moveTo((float)v.getX(), (float)v.getY());

    while (it.hasNext()) {
      v = it.next();
      result.lineTo((float)v.getX(), (float)v.getY());
    }

    result.closePath();

    return result;
  }
  
  /**
   * Create the encapsulated AWT Polygon (a GeneralPath).
   *
   * @param params  parameters for this Shape as read from the config file
   *
   * @return A GeneralPath.
   *
   * @throws IllegalArgumentException if less than three vertices are specified
   *         on the given params object.
   */
  protected java.awt.Shape createAWTShape(Element params) {
    GeneralPath result = null;
    NodeList verticeNodes = getVertices(params);
    
    ArrayList<Vertice> vertices = new ArrayList<Vertice>();
      
    try {
      for (int i=0; i<verticeNodes.getLength(); i++) {
        vertices.add(new Vertice(verticeNodes.item(i)));
      }
    }
    catch(IllegalArgumentException e) {
      throw new IllegalArgumentException("Polygon: " + e.getMessage());
    }

    return makePolygon(vertices);
  }

  /*
   * Get the list of vertices for this polygon. Cannot use getElementsByTagName
   * because it returns subchild vertices.
   */
  private NodeList getVertices(Element params) {
    NodeList allNodes = params.getChildNodes();
    ParamList vertices = new ParamList();

    for (int i=0; i<allNodes.getLength(); i++) {
      Node n = allNodes.item(i);
      if (n.getNodeName().compareTo("Vertice") == 0) {
        vertices.add(n);
      }
    }

    return vertices;
  }
  
  /**
   * Get the list of child nodes in params.
   *
   * @return The child node list.
   */
  protected NodeList getChildren(Element params) {
    // Vertices are not children, filter out
    NodeList allNodes = params.getChildNodes();
    ParamList children = new ParamList();

    for (int i=0; i<allNodes.getLength(); i++) {
      Node n = allNodes.item(i);
      if (n.getNodeName().compareTo("Vertice") != 0) {
        children.add(n);
      }
    }

    return children;
  }

  /*
   * Nested class used for a Polygon vertice.
   */
  public static class Vertice extends Point2D.Double {
    public Vertice(double x, double y) {
      super(x, y);
    }
    
    public Vertice(Node params) {
      super();
      
      NamedNodeMap attributes = params.getAttributes();
      
      if (attributes == null) {
        throw new IllegalArgumentException("Vertice requires the x and y attributes");
      }
      else {
        try {
          ShapeAttributes shapeAttributes = new ShapeAttributes(attributes);
          setLocation(shapeAttributes.getDouble("x"),
                      shapeAttributes.getDouble("y"));
        }
        catch(IllegalArgumentException e) {
          throw new IllegalArgumentException("Vertice: " + e.getMessage());
        }
      }
    }
  }

  /*
   * Static block of code used to register a ShapeFactory for Polygons.
   */
  static {
    Shape.registerShapeFactory(
      "Polygon",
      new Shape.ShapeFactory() {
        public Shape makeShape(Shape parent, Element params) {
          return new Polygon(parent, params);
        }
      });
  }
  
}